// FILE: main.cpp by Richard Hughes - 27/04/2012
//
// DESC: The mail entry point into this test app

// INCLUDES
#include <iostream>
#include <string>
#include <conio.h>
#include <stdint.h>
#include <fstream>
#include <sstream>
#include <Chip8Emulator.h>
#include <Chip8Disassembler.h>
#include "Chip8ConsoleDisplay.h"
#include "Chip8ConsoleKeyboard.h"
#include "Chip8ConsoleBuzzer.h"

// NAMESPACES
using namespace std;
using namespace Emulator::Chip8;

void WriteDebugInfo(CHAR_INFO* buff, int x, int y, int width, string s)
{
	s += "     "; // get ride of any old info - its rather confusing otherwise...

	int i = 0;
	for(int posX = 0; posX < s.length(); posX++)
	{
		buff[posX + x + (y * width)].Char.AsciiChar = s.c_str()[i++];
		buff[posX + x + (y * width)].Attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
	}
}

// the main entry point into this app
// it takes the number of command line arguments and the arguments themselves
// returns 1
int main(int argv, char* args[])
{
	Chip8Emulator emu;

	Chip8ConsoleDisplay display;
	emu.SetDisplay(&display);

	Chip8ConsoleKeyboard keyboard;
	emu.SetKeyboard(&keyboard);

	Chip8ConsoleBuzzer buzzer;
	emu.SetBuzzer(&buzzer);

	CHAR_INFO debugInfo[80 * 100];

	cout << "Creating emulator" << endl;
	if(!emu.Init())
	{
		cout << "Failed to init emulator" << endl;
		return 0;
	}

	cout << "Successfully initiated emulator" << endl;

	string file;
	cout << "Please enter filename: ";
	cin >> file;

	string breakpoint;
	cout << "Please enter breakpoint: ";
	cin >> breakpoint;
	uint16_t bp = 0;
	stringstream ss;
	ss << std::hex << breakpoint;
	ss >> bp;

	emu.LoadROM(file);

	if(bp > 0)
		emu.GetDebugger()->AddBreakpoint(bp);

	Chip8Disassembler dis;
	string code = dis.Disassemble(emu.GetROM());
	ofstream fs;
	fs.open("code.ch8asm");
	fs << code;
	fs.close();

	HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
	display.Init(consoleHandle);

	while(emu.Frame())
	{
		stringstream ss;

		if(!emu.GetDebugger()->IsAttached())
			continue;

		ss << "Debugger Attached: " << emu.GetDebugger()->IsAttached() ? "true" : "false";
		WriteDebugInfo(debugInfo, 2, 2, 80, ss.str());
		ss.clear();
		ss.str(string());

		ss << "Address: 0x" << std::hex << emu.GetCPU()->GetPC();
		WriteDebugInfo(debugInfo, 36, 2, 80, ss.str());
		ss.clear();
		ss.str(string());

		uint16_t op = emu.GetRAM()->Read16Bit(emu.GetCPU()->GetPC());
		uint16_t swap = (op << 8) | (op >> 8);

		ss << "PC: 0x" << std::hex << emu.GetCPU()->GetPC();
		WriteDebugInfo(debugInfo, 2, 4, 80, ss.str());
		ss.clear();
		ss.str(string());

		for(int i = 0; i < 16; i++)
		{
			ss << "V" << std::hex << i << ": 0x" << std::hex << (int)emu.GetCPU()->GetVF(i); // cast to int so it is not treated as a char
			WriteDebugInfo(debugInfo, 2, 6 + i, 80, ss.str());
			ss.clear();
			ss.str(string());
		}

		for(int i = 0; i < 16; i++)
		{
			ss << "Stack[" << std::dec << i << "]: 0x" << std::hex << emu.GetCPU()->GetStack()[i];
			WriteDebugInfo(debugInfo, 36, 6 + i, 80, ss.str());
			ss.clear();
			ss.str(string());
		}

		ss << "Instruction: " << emu.GetDisassembler()->Disassemble((uint8_t*)&swap, 2);
		WriteDebugInfo(debugInfo, 36, 4, 80, ss.str());
		ss.clear();
		ss.str(string());

		ss << "I: 0x" << std::hex << emu.GetCPU()->GetI();
		WriteDebugInfo(debugInfo, 2, 23, 80, ss.str());
		ss.clear();
		ss.str(string());

		ss << "SP: 0x" << std::hex << (int)emu.GetCPU()->GetSP();
		WriteDebugInfo(debugInfo, 36, 23, 80, ss.str());
		ss.clear();
		ss.str(string());

		ss << "Timer: " << std::dec << (int)emu.GetCPU()->GetTimer();
		WriteDebugInfo(debugInfo, 2, 25, 80, ss.str());
		ss.clear();
		ss.str(string());

		ss << "Sound: " << std::dec << (int)emu.GetCPU()->GetSound();
		WriteDebugInfo(debugInfo, 36, 25, 80, ss.str());
		ss.clear();
		ss.str(string());

		COORD bufferSize = { 80, 100 };
		COORD c = { 0, 0 };
		SMALL_RECT rect = { 0, 34, 80, 135 };
		WriteConsoleOutputA(consoleHandle, debugInfo, bufferSize, c, &rect);

		//if(getchar() == 'q')
			//break;
	}

	cout << "Shutting down emulator" << endl;
	if(!emu.Shutdown())
	{
		cout << "Failed to shutdown emulator" << endl;
		return 0;
	}

	cout << "Successfully shutdown emulator" << endl;

	while(!_kbhit())
		;

	return 1;

} // end main

// EOF: main.cpp